/*
 * This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only.  Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package com.image.fresco.zoomable;

import android.app.Activity;
import android.content.Context;
import android.graphics.PointF;
import android.view.GestureDetector;
import android.view.MotionEvent;

/**
 * Tap gesture listener for double tap to zoom / unzoom and double-tap-and-drag to zoom.
 *
 * @see ZoomableDraweeView#setTapListener(GestureDetector.SimpleOnGestureListener)
 */
public class DoubleTapGestureListener extends GestureDetector.SimpleOnGestureListener {
    private static final int DURATION_MS = 300;
    private static final int DOUBLE_TAP_SCROLL_THRESHOLD = 20;

    private Context mContext;

    private final ZoomableDraweeView mDraweeView;
    private final PointF mDoubleTapViewPoint = new PointF();
    private final PointF mDoubleTapImagePoint = new PointF();
    private float mDoubleTapScale = 1;
    private boolean mDoubleTapScroll = false;

    public DoubleTapGestureListener(Context context, ZoomableDraweeView zoomableDraweeView) {
        mContext = context;
        mDraweeView = zoomableDraweeView;
    }

    @Override
    public void onLongPress(MotionEvent e) {
        super.onLongPress(e);
        if (mContext != null && mContext instanceof Callback) {
            ((Callback) mContext).onLongClick(mDraweeView);
        }
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        if (mContext != null && mContext instanceof Callback) {
            ((Callback) mContext).onClick(mDraweeView);
        }
        return super.onSingleTapConfirmed(e);
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        AbstractAnimatedZoomableController zc = (AbstractAnimatedZoomableController) mDraweeView.getZoomableController();
        PointF vp = new PointF(e.getX(), e.getY());
        PointF ip = zc.mapViewToImage(vp);
        switch (e.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mDoubleTapViewPoint.set(vp);
                mDoubleTapImagePoint.set(ip);
                mDoubleTapScale = zc.getScaleFactor();
                break;
            case MotionEvent.ACTION_MOVE:
                mDoubleTapScroll = mDoubleTapScroll || shouldStartDoubleTapScroll(vp);
                if (mDoubleTapScroll) {
                    float scale = calcScale(vp);
                    zc.zoomToPoint(scale, mDoubleTapImagePoint, mDoubleTapViewPoint);
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mDoubleTapScroll) {
                    float scale = calcScale(vp);
                    zc.zoomToPoint(scale, mDoubleTapImagePoint, mDoubleTapViewPoint);
                } else {
                    final float maxScale = zc.getMaxScaleFactor();
                    final float middScale = zc.getMiddScaleFactor();
                    final float minScale = zc.getMinScaleFactor();

                    if (zc.getScaleFactor() >= maxScale) {
                        zc.zoomToPoint(minScale,
                                       ip,
                                       vp,
                                       DefaultZoomableController.LIMIT_ALL,
                                       DURATION_MS,
                                       null);
                        if (mContext != null && mContext instanceof Callback) {
                            ((Callback) mContext).onDoubleTapScale(0);
                        }
                    } else if (zc.getScaleFactor() >= middScale) {
                        zc.zoomToPoint(maxScale,
                                       ip,
                                       vp,
                                       DefaultZoomableController.LIMIT_ALL,
                                       DURATION_MS,
                                       null);
                        if (mContext != null && mContext instanceof Callback) {
                            ((Callback) mContext).onDoubleTapScale(2);
                        }
                    } else {
                        zc.zoomToPoint(middScale,
                                       ip,
                                       vp,
                                       DefaultZoomableController.LIMIT_ALL,
                                       DURATION_MS,
                                       null);
                        if (mContext != null && mContext instanceof Callback) {
                            ((Callback) mContext).onDoubleTapScale(1);
                        }
                    }
                }
                mDoubleTapScroll = false;
                break;
        }
        return true;
    }

    private boolean shouldStartDoubleTapScroll(PointF viewPoint) {
        double dist = Math.hypot(viewPoint.x - mDoubleTapViewPoint.x,
                                 viewPoint.y - mDoubleTapViewPoint.y);
        return dist > DOUBLE_TAP_SCROLL_THRESHOLD;
    }

    private float calcScale(PointF currentViewPoint) {
        float dy = (currentViewPoint.y - mDoubleTapViewPoint.y);
        float t = 1 + Math.abs(dy) * 0.001f;
        return (dy < 0) ? mDoubleTapScale / t : mDoubleTapScale * t;
    }

    public interface Callback {
        void onLongClick(ZoomableDraweeView draweeView);

        /**
         * @param type 0，min；1，middle；2，max
         */
        void onDoubleTapScale(int type);

        void onClick(ZoomableDraweeView draweeView);
    }
}
